# Copyright (c) 2020 ARM Limited
# All rights reserved
#
# The license below extends only to copyright in the software and shall
# not be construed as granting a license to any other intellectual
# property including but not limited to intellectual property relating
# to a hardware implementation of the functionality of the software
# licensed hereunder.  You may use the software subject to the license
# terms below provided that you ensure that this notice is replicated
# unmodified and in its entirety in all distributions of the software,
# modified or unmodified, in source code or in binary form.
#
# Copyright 2019 Google, Inc.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met: redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer;
# redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution;
# neither the name of the copyright holders nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

from m5.objects.FastModel import (
    AmbaInitiatorSocket,
    AmbaTargetSocket,
)
from m5.objects.Gic import BaseGic
from m5.objects.IntPin import VectorIntSourcePin
from m5.objects.ResetPort import ResetResponsePort
from m5.objects.SystemC import SystemC_ScModule
from m5.params import *
from m5.SimObject import SimObject
from m5.util.fdthelper import *

GICV3_COMMS_TARGET_ROLE = "GICV3 COMMS TARGET"
GICV3_COMMS_INITIATOR_ROLE = "GICV3 COMMS INITIATOR"

Port.compat(GICV3_COMMS_TARGET_ROLE, GICV3_COMMS_INITIATOR_ROLE)


class Gicv3CommsTargetSocket(Port):
    def __init__(self, desc):
        super().__init__(GICV3_COMMS_INITIATOR_ROLE, desc)


class Gicv3CommsInitiatorSocket(Port):
    def __init__(self, desc):
        super().__init__(GICV3_COMMS_TARGET_ROLE, desc, is_source=True)


class VectorGicv3CommsInitiatorSocket(VectorPort):
    def __init__(self, desc):
        super().__init__(GICV3_COMMS_TARGET_ROLE, desc, is_source=True)


class SCFastModelGIC(SystemC_ScModule):
    type = "SCFastModelGIC"
    cxx_class = "gem5::fastmodel::SCGIC"
    cxx_header = "arch/arm/fastmodel/GIC/gic.hh"

    enabled = Param.Bool(
        True,
        "Enable GICv3 functionality; when false the "
        "component is inactive. has_gicv3 will replace this when GIC_IRI "
        "replaces GICv3IRI.",
    )
    has_gicv3 = Param.Bool(
        False,
        "Enable GICv3 functionality; when false "
        'the component is inactive. This will replace "enabled" '
        "parameter.",
    )
    has_gicv4_1 = Param.Bool(
        False,
        "Enable GICv4.1 functionality; when "
        "false the component is inactive.",
    )
    vPEID_bits = Param.Unsigned(16, "Number of bits of vPEID with GICv4.1.")
    print_mmap = Param.Bool(False, "Print memory map to stdout")
    monolithic = Param.Bool(
        False, "Indicate that the implementation is not distributed"
    )
    direct_lpi_support = Param.Bool(
        False, "Enable support for LPI operations through GICR registers"
    )
    cpu_affinities = Param.String(
        "",
        "A comma separated list of dotted quads "
        "containing the affinities of all PEs connected to this IRI.",
    )
    non_ARE_core_count = Param.Unsigned(
        8,
        "Maximum number of non-ARE cores; "
        "normally used to pass the cluster-level NUM_CORES parameter to "
        "the top-level redistributor.",
    )
    reg_base = Param.Addr(0x2C010000, "Base for decoding GICv3 registers.")
    reg_base_per_redistributor = Param.String(
        "",
        "Base address for each "
        "redistributor in the form "
        "'0.0.0.0=0x2c010000, 0.0.0.1=0x2c020000'.  All redistributors "
        "must be specified and this overrides the reg-base parameter "
        "(except that reg-base will still be used for the top-level "
        "redistributor).",
    )
    gicd_alias = Param.Addr(
        0x0,
        "In GICv2 mode: the base address for a 4k "
        "page alias of the first 4k of the Distributor page, in GICv3 "
        "mode. the base address of a 64KB page containing message based "
        "SPI signalling register aliases(0:Disabled)",
    )
    has_two_security_states = Param.Bool(
        True, "If true, has two security states"
    )
    DS_fixed_to_zero = Param.Bool(
        False, "Enable/disable support of single security state"
    )
    IIDR = Param.UInt32(0x0, "GICD_IIDR and GICR_IIDR value")
    gicv2_only = Param.Bool(
        False,
        "If true, when using the GICv3 model, pretend to be a GICv2 system",
    )
    STATUSR_implemented = Param.Bool(
        True, "Determines whether the GICR_STATUSR register is implemented."
    )
    priority_bits_implemented = Param.Unsigned(
        5, "Number of implemented priority bits"
    )
    itargets_razwi = Param.Bool(
        False, "If true, the GICD_ITARGETS registers are RAZ/WI"
    )
    icfgr_sgi_mask = Param.UInt32(
        0x0, "Mask for writes to ICFGR registers that configure SGIs"
    )
    icfgr_ppi_mask = Param.UInt32(
        0xAAAAAAAA, "Mask for writes to ICFGR registers that configure PPIs"
    )
    icfgr_spi_mask = Param.UInt32(
        0xAAAAAAAA, "Mask for writes to ICFGR registers that configure SPIs"
    )
    icfgr_sgi_reset = Param.UInt32(
        0xAAAAAAAA, "Reset value for ICFGR registers that configure SGIs"
    )
    icfgr_ppi_reset = Param.UInt32(
        0x0, "Reset value for ICFGR regesters that configure PPIs"
    )
    icfgr_spi_reset = Param.UInt32(
        0x0, "Reset value for ICFGR regesters that configure SPIs"
    )
    icfgr_ppi_rsvd_bit = Param.Bool(
        False,
        "If ARE=0, the value of reserved "
        "bits i.e. bit 0,2,4..30 of ICFGRn for n>0",
    )
    igroup_sgi_mask = Param.UInt16(
        0xFFFF, "Mask for writes to SGI bits in IGROUP registers"
    )
    igroup_ppi_mask = Param.UInt16(
        0xFFFF, "Mask for writes to PPI bits in IGROUP registers"
    )
    igroup_sgi_reset = Param.UInt16(
        0x0, "Reset value for SGI bits in IGROUP registers"
    )
    igroup_ppi_reset = Param.UInt16(
        0x0, "Reset value for SGI bits in IGROUP registers"
    )
    ppi_implemented_mask = Param.UInt16(
        0xFFFF,
        "Mask of PPIs that are "
        "implemented. One bit per PPI bit 0 == PPI 16 (first PPI). This "
        "will affect other masks.",
    )
    spi_count = Param.UInt16(224, "Number of SPIs that are implemented.")
    lockable_spi_count = Param.Unsigned(
        0,
        "Number of SPIs that are locked "
        "down when CFGSDISABLE signal is asserted.  Only applies for "
        "GICv2.",
    )
    iri_id_bits = Param.Unsigned(
        16,
        "Number of bits used to represent "
        "interrupts IDs in the Distributor and Redistributors, forced to "
        "10 if LPIs are not supported",
    )
    delay_redistributor_accesses = Param.Bool(
        True,
        "Delay memory accesses "
        "from the redistributor until GICR_SYNCR is read.",
    )
    gicd_pidr = Param.UInt64(
        0x0,
        "The value for the GICD_PIDR registers, if "
        "non-zero. Note: fixed fields (device type etc.) will be "
        "overriden in this value.",
    )
    gicr_pidr = Param.UInt64(
        0x0,
        "The value for the GICR_PIDR registers, if "
        "non-zero. Note: fixed fields (device type etc.) will be "
        "overriden in this value.",
    )
    its_count = Param.Unsigned(
        0,
        "Number of Interrupt Translation Services "
        "to be instantiated (0=none)",
    )
    its0_base = Param.Addr(
        0, "Register base address for ITS0 (automatic if 0)."
    )
    its1_base = Param.Addr(
        0, "Register base address for ITS1 (automatic if 0)."
    )
    its2_base = Param.Addr(
        0, "Register base address for ITS2 (automatic if 0)."
    )
    its3_base = Param.Addr(
        0, "Register base address for ITS3 (automatic if 0)."
    )
    gits_pidr = Param.UInt64(
        0x0,
        "The value for the GITS_PIDR registers, if "
        "non-zero. Note: fixed fields (device type etc.) will be "
        "overriden in this value.",
    )
    gits_baser0_type = Param.Unsigned(
        0,
        "Type field for GITS_BASER0 "
        "register. 0 = Unimplemented; 1 = Devices; "
        "2 = Virtual Processors; 3 = Physical Processors; 4 = Collections",
    )
    gits_baser1_type = Param.Unsigned(
        0,
        "Type field for GITS_BASER1 "
        "register. 0 = Unimplemented; 1 = Devices; "
        "2 = Virtual Processors; 3 = Physical Processors; 4 = Collections",
    )
    gits_baser2_type = Param.Unsigned(
        0,
        "Type field for GITS_BASER2 "
        "register. 0 = Unimplemented; 1 = Devices; "
        "2 = Virtual Processors; 3 = Physical Processors; 4 = Collections",
    )
    gits_baser3_type = Param.Unsigned(
        0,
        "Type field for GITS_BASER3 "
        "register. 0 = Unimplemented; 1 = Devices; "
        "2 = Virtual Processors; 3 = Physical Processors; 4 = Collections",
    )
    gits_baser4_type = Param.Unsigned(
        0,
        "Type field for GITS_BASER4 "
        "register. 0 = Unimplemented; 1 = Devices; "
        "2 = Virtual Processors; 3 = Physical Processors; 4 = Collections",
    )
    gits_baser5_type = Param.Unsigned(
        0,
        "Type field for GITS_BASER5 "
        "register. 0 = Unimplemented; 1 = Devices; "
        "2 = Virtual Processors; 3 = Physical Processors; 4 = Collections",
    )
    gits_baser6_type = Param.Unsigned(
        0,
        "Type field for GITS_BASER6 "
        "register. 0 = Unimplemented; 1 = Devices; "
        "2 = Virtual Processors; 3 = Physical Processors; 4 = Collections",
    )
    gits_baser7_type = Param.Unsigned(
        0,
        "Type field for GITS_BASER7 "
        "register. 0 = Unimplemented; 1 = Devices; "
        "2 = Virtual Processors; 3 = Physical Processors; 4 = Collections",
    )
    gits_baser0_entry_bytes = Param.Unsigned(
        8, "Number of bytes required per entry for GITS_BASER0 register."
    )
    gits_baser1_entry_bytes = Param.Unsigned(
        8, "Number of bytes required per entry for GITS_BASER1 register."
    )
    gits_baser2_entry_bytes = Param.Unsigned(
        8, "Number of bytes required per entry for GITS_BASER2 register."
    )
    gits_baser3_entry_bytes = Param.Unsigned(
        8, "Number of bytes required per entry for GITS_BASER3 register."
    )
    gits_baser4_entry_bytes = Param.Unsigned(
        8, "Number of bytes required per entry for GITS_BASER4 register."
    )
    gits_baser5_entry_bytes = Param.Unsigned(
        8, "Number of bytes required per entry for GITS_BASER5 register."
    )
    gits_baser6_entry_bytes = Param.Unsigned(
        8, "Number of bytes required per entry for GITS_BASER6 register."
    )
    gits_baser7_entry_bytes = Param.Unsigned(
        8, "Number of bytes required per entry for GITS_BASER7 register."
    )
    gits_baser0_indirect_raz = Param.Bool(
        False, "Indirect field for GITS_BASER0 register is RAZ/WI."
    )
    gits_baser1_indirect_raz = Param.Bool(
        False, "Indirect field for GITS_BASER1 register is RAZ/WI."
    )
    gits_baser2_indirect_raz = Param.Bool(
        False, "Indirect field for GITS_BASER2 register is RAZ/WI."
    )
    gits_baser3_indirect_raz = Param.Bool(
        False, "Indirect field for GITS_BASER3 register is RAZ/WI."
    )
    gits_baser4_indirect_raz = Param.Bool(
        False, "Indirect field for GITS_BASER4 register is RAZ/WI."
    )
    gits_baser5_indirect_raz = Param.Bool(
        False, "Indirect field for GITS_BASER5 register is RAZ/WI."
    )
    gits_baser6_indirect_raz = Param.Bool(
        False, "Indirect field for GITS_BASER6 register is RAZ/WI."
    )
    gits_baser7_indirect_raz = Param.Bool(
        False, "Indirect field for GITS_BASER7 register is RAZ/WI."
    )
    its_baser_force_page_alignement = Param.Bool(
        True,
        "Force alignement of "
        "address writen to a GITS_BASER register to the page size "
        "configured",
    )
    processor_numbers = Param.String(
        "",
        "Specify processor numbers (as "
        "appears in GICR_TYPER) in the form 0.0.0.0=0,0.0.0.1=1 etc.) If "
        "not specified, will number processors starting at 0.",
    )
    supports_shareability = Param.Bool(
        True,
        "Device supports shareability "
        "attributes on outgoing memory bus (i.e. is modelling an ACElite "
        "port rather than an AXI4 port).",
    )
    a3_affinity_supported = Param.Bool(
        False, "Device supports affinity level 3 values that are non-zero."
    )
    SGI_RSS_support = Param.Bool(
        False, "Device has support for the Range Selector feature for SGI"
    )
    gicr_propbaser_read_only = Param.Bool(
        False, "GICR_PROPBASER register is read-only."
    )
    gicr_propbaser_reset = Param.UInt64(
        0x0, "Value of GICR_PROPBASER on reset."
    )
    its_device_bits = Param.Unsigned(
        16, "Number of bits supported for ITS device IDs."
    )
    its_entry_size = Param.Unsigned(
        8, "Number of bytes required to store each entry in the ITT tables."
    )
    its_id_bits = Param.Unsigned(
        16, "Number of interrupt bits supported by ITS."
    )
    its_collection_id_bits = Param.Unsigned(
        0,
        "Number of collection bits "
        "supported by ITS (optional parameter, 0 => 16bits support and "
        "GITS_TYPER.CIL=0",
    )
    its_cumulative_collection_tables = Param.Bool(
        True,
        "When true, the "
        "supported amount of collections is the sum of GITS_TYPER.HCC and "
        "the number of collections supported in memory, otherwise, simply "
        "the number supported in memory only. Irrelevant when HCC=0",
    )
    delay_ITS_accesses = Param.Bool(
        True, "Delay accesses from the ITS until GICR_SYNCR is read."
    )
    local_SEIs = Param.Bool(False, "Generate SEI to signal internal issues")
    local_VSEIs = Param.Bool(False, "Generate VSEI to signal internal issues")
    ITS_use_physical_target_addresses = Param.Bool(
        True,
        "Use physical "
        "hardware adresses for targets in ITS commands -- must be true "
        "for distributed implementations",
    )
    ITS_hardware_collection_count = Param.Unsigned(
        0, "Number of hardware collections held exclusively in the ITS"
    )
    ITS_MOVALL_update_collections = Param.Bool(
        False, "Whether MOVALL command updates the collection entires"
    )
    ITS_TRANSLATE64R = Param.Bool(
        False,
        "Add an implementation specific "
        "register at 0x10008 supporting 64 bit TRANSLATER (dev[63:32], "
        "interupt[31:0])",
    )
    enable_protocol_checking = Param.Bool(
        False, "Enable/disable protocol checking at cpu interface"
    )
    fixed_routed_spis = Param.String(
        "",
        "Value of IROUTER[n] register in the "
        "form 'n=a.b.c.d, n=*'. The RM bit of IROUTER is 0 when n=a.b.c.d "
        "is used else 1 when n=* is used. n can be >= 32 and <= 1019",
    )
    irouter_default_mask = Param.String(
        "",
        "Default Mask value for "
        "IROUTER[32..1019] register in the form 'a.b.c.d'",
    )
    irouter_default_reset = Param.String(
        "",
        "Default Reset Value of "
        "IROUTER[32..1019] register in the form 'a.b.c.d' or *",
    )
    irouter_reset_values = Param.String(
        "",
        "Reset Value of IROUTER[n] "
        "register in the form 'n=a.b.c.d or n=*'.n can be >= 32 and "
        "<= 1019",
    )
    irouter_mask_values = Param.String(
        "",
        "Mask Value of IROUTER[n] register "
        "in the form 'n=a.b.c.d'.n can be >= 32 and <= 1019",
    )
    ITS_threaded_command_queue = Param.Bool(
        True,
        "Enable execution of ITS "
        "commands in a separate thread which is sometimes required for "
        "cosimulation",
    )
    ITS_legacy_iidr_typer_offset = Param.Bool(
        False,
        "Put the GITS_IIDR and "
        "GITS_TYPER registers at their older offset of 0x8 and 0x4 "
        "respectively",
    )
    redistributor_threaded_command_queue = Param.Bool(
        True,
        "Enable execution "
        "of redistributor delayed transactions in a separate thread which "
        "is sometimes required for cosimulation",
    )
    ignore_generate_sgi_when_no_are = Param.Bool(
        False,
        "Ignore GenerateSGI "
        "packets coming form the CPU interface if both ARE_S and ARE_NS "
        "are 0",
    )
    trace_speculative_lpi_property_updates = Param.Bool(
        False,
        "Trace LPI "
        "propery updates performed on speculative accesses (useful for "
        "debuging LPI)",
    )
    virtual_lpi_support = Param.Bool(
        False,
        "GICv4 Virtual LPIs and Direct injection of Virtual LPIs supported",
    )
    virtual_priority_bits = Param.Unsigned(
        5, "Number of implemented virtual priority bits"
    )
    LPI_cache_type = Param.Unsigned(
        1, "Cache type for LPIs, 0:No caching, 1:Full caching"
    )
    LPI_cache_check_data = Param.Bool(
        False,
        "Enable Cached LPI data against "
        "memory checking when available for cache type",
    )
    DPG_bits_implemented = Param.Bool(
        False,
        "Enable implementation of "
        "interrupt group participation bits or DPG bits in GICR_CTLR",
    )
    DPG_ARE_only = Param.Bool(
        False,
        "Limit application of DPG bits to interrupt groups for which ARE=1",
    )
    ARE_fixed_to_one = Param.Bool(
        False,
        "GICv2 compatibility is not "
        "supported and GICD_CTLR.ARE_* is always one",
    )
    legacy_sgi_enable_rao = Param.Bool(
        False, "Enables for SGI associated with an ARE=0 regime are RAO/WI"
    )
    pa_size = Param.Unsigned(48, "Number of valid bits in physical address")
    MSI_IIDR = Param.UInt32(0x0, "Value returned in MSI_IIDR registers.")
    MSI_NS_frame0_base = Param.Addr(
        0x0,
        "If non-zero, sets the base "
        "address used for non-secure MSI frame 0 registers.",
    )
    MSI_NS_frame0_max_SPI = Param.UInt16(
        0,
        "Maximum SPI ID supported by "
        "non-secure MSI frame 0. Set to 0 to disable frame.",
    )
    MSI_NS_frame0_min_SPI = Param.UInt16(
        0,
        "Minimum SPI ID supported by "
        "non-secure MSI frame 0. Set to 0 to disable frame.",
    )
    MSI_NS_frame1_base = Param.Addr(
        0x0,
        "If non-zero, sets the base "
        "address used for non-secure MSI frame 1 registers.",
    )
    MSI_NS_frame1_max_SPI = Param.UInt16(
        0,
        "Maximum SPI ID supported by "
        "non-secure MSI frame 1. Set to 0 to disable frame.",
    )
    MSI_NS_frame1_min_SPI = Param.UInt16(
        0,
        "Minimum SPI ID supported by "
        "non-secure MSI frame 1. Set to 0 to disable frame.",
    )
    MSI_NS_frame2_base = Param.Addr(
        0x0,
        "If non-zero, sets the base address "
        "used for non-secure MSI frame 2 registers.",
    )
    MSI_NS_frame2_max_SPI = Param.UInt16(
        0,
        "Maximum SPI ID supported by "
        "non-secure MSI frame 2. Set to 0 to disable frame.",
    )
    MSI_NS_frame2_min_SPI = Param.UInt16(
        0,
        "Minimum SPI ID supported by "
        "non-secure MSI frame 2. Set to 0 to disable frame.",
    )
    MSI_NS_frame3_base = Param.Addr(
        0x0,
        "If non-zero, sets the base address "
        "used for non-secure MSI frame 3 registers.",
    )
    MSI_NS_frame3_max_SPI = Param.UInt16(
        0,
        "Maximum SPI ID supported by "
        "non-secure MSI frame 3. Set to 0 to disable frame.",
    )
    MSI_NS_frame3_min_SPI = Param.UInt16(
        0,
        "Minimum SPI ID supported by "
        "non-secure MSI frame 3. Set to 0 to disable frame.",
    )
    MSI_NS_frame4_base = Param.Addr(
        0x0,
        "If non-zero, sets the base address "
        "used for non-secure MSI frame 4 registers.",
    )
    MSI_NS_frame4_max_SPI = Param.UInt16(
        0,
        "Maximum SPI ID supported by "
        "non-secure MSI frame 4. Set to 0 to disable frame.",
    )
    MSI_NS_frame4_min_SPI = Param.UInt16(
        0,
        "Minimum SPI ID supported by "
        "non-secure MSI frame 4. Set to 0 to disable frame.",
    )
    MSI_NS_frame5_base = Param.Addr(
        0x0,
        "If non-zero, sets the base address "
        "used for non-secure MSI frame 5 registers.",
    )
    MSI_NS_frame5_max_SPI = Param.UInt16(
        0,
        "Maximum SPI ID supported by "
        "non-secure MSI frame 5. Set to 0 to disable frame.",
    )
    MSI_NS_frame5_min_SPI = Param.UInt16(
        0,
        "Minimum SPI ID supported by "
        "non-secure MSI frame 5. Set to 0 to disable frame.",
    )
    MSI_NS_frame6_base = Param.Addr(
        0x0,
        "If non-zero, sets the base address "
        "used for non-secure MSI frame 6 registers.",
    )
    MSI_NS_frame6_max_SPI = Param.UInt16(
        0,
        "Maximum SPI ID supported by "
        "non-secure MSI frame 6. Set to 0 to disable frame.",
    )
    MSI_NS_frame6_min_SPI = Param.UInt16(
        0,
        "Minimum SPI ID supported by "
        "non-secure MSI frame 6. Set to 0 to disable frame.",
    )
    MSI_NS_frame7_base = Param.Addr(
        0x0,
        "If non-zero, sets the base address "
        "used for non-secure MSI frame 7 registers.",
    )
    MSI_NS_frame7_max_SPI = Param.UInt16(
        0,
        "Maximum SPI ID supported by "
        "non-secure MSI frame 7. Set to 0 to disable frame.",
    )
    MSI_NS_frame7_min_SPI = Param.UInt16(
        0,
        "Minimum SPI ID supported by "
        "non-secure MSI frame 7. Set to 0 to disable frame.",
    )
    MSI_PIDR = Param.UInt64(
        0x0,
        "The value for the MSI_PIDR registers, if "
        "non-zero and distributor supports GICv2m. Note: fixed fields "
        "(device type etc.) will be overriden in this value.",
    )
    MSI_S_frame0_base = Param.Addr(
        0x0,
        "If non-zero, sets the base address "
        "used for secure MSI frame 0 registers.",
    )
    MSI_S_frame0_max_SPI = Param.UInt16(
        0,
        "Maximum SPI ID supported by "
        "secure MSI frame 0. Set to 0 to disable frame.",
    )
    MSI_S_frame0_min_SPI = Param.UInt16(
        0,
        "Minimum SPI ID supported by "
        "secure MSI frame 0. Set to 0 to disable frame.",
    )
    MSI_S_frame1_base = Param.Addr(
        0x0,
        "If non-zero, sets the base address "
        "used for secure MSI frame 1 registers.",
    )
    MSI_S_frame1_max_SPI = Param.UInt16(
        0,
        "Maximum SPI ID supported by "
        "secure MSI frame 1. Set to 0 to disable frame.",
    )
    MSI_S_frame1_min_SPI = Param.UInt16(
        0,
        "Minimum SPI ID supported by "
        "secure MSI frame 1. Set to 0 to disable frame.",
    )
    MSI_S_frame2_base = Param.Addr(
        0x0,
        "If non-zero, sets the base address "
        "used for secure MSI frame 2 registers.",
    )
    MSI_S_frame2_max_SPI = Param.UInt16(
        0,
        "Maximum SPI ID supported by "
        "secure MSI frame 2. Set to 0 to disable frame.",
    )
    MSI_S_frame2_min_SPI = Param.UInt16(
        0,
        "Minimum SPI ID supported by "
        "secure MSI frame 2. Set to 0 to disable frame.",
    )
    MSI_S_frame3_base = Param.Addr(
        0x0,
        "If non-zero, sets the base address "
        "used for secure MSI frame 3 registers.",
    )
    MSI_S_frame3_max_SPI = Param.UInt16(
        0,
        "Maximum SPI ID supported by "
        "secure MSI frame 3. Set to 0 to disable frame.",
    )
    MSI_S_frame3_min_SPI = Param.UInt16(
        0,
        "Minimum SPI ID supported by "
        "secure MSI frame 3. Set to 0 to disable frame.",
    )
    MSI_S_frame4_base = Param.Addr(
        0x0,
        "If non-zero, sets the base address "
        "used for secure MSI frame 4 registers.",
    )
    MSI_S_frame4_max_SPI = Param.UInt16(
        0,
        "Maximum SPI ID supported by "
        "secure MSI frame 4. Set to 0 to disable frame.",
    )
    MSI_S_frame4_min_SPI = Param.UInt16(
        0,
        "Minimum SPI ID supported by "
        "secure MSI frame 4. Set to 0 to disable frame.",
    )
    MSI_S_frame5_base = Param.Addr(
        0x0,
        "If non-zero, sets the base address "
        "used for secure MSI frame 5 registers.",
    )
    MSI_S_frame5_max_SPI = Param.UInt16(
        0,
        "Maximum SPI ID supported by "
        "secure MSI frame 5. Set to 0 to disable frame.",
    )
    MSI_S_frame5_min_SPI = Param.UInt16(
        0,
        "Minimum SPI ID supported by "
        "secure MSI frame 5. Set to 0 to disable frame.",
    )
    MSI_S_frame6_base = Param.Addr(
        0x0,
        "If non-zero, sets the base address "
        "used for secure MSI frame 6 registers.",
    )
    MSI_S_frame6_max_SPI = Param.UInt16(
        0,
        "Maximum SPI ID supported by "
        "secure MSI frame 6. Set to 0 to disable frame.",
    )
    MSI_S_frame6_min_SPI = Param.UInt16(
        0,
        "Minimum SPI ID supported by "
        "secure MSI frame 6. Set to 0 to disable frame.",
    )
    MSI_S_frame7_base = Param.Addr(
        0x0,
        "If non-zero, sets the base address "
        "used for secure MSI frame 7 registers.",
    )
    MSI_S_frame7_max_SPI = Param.UInt16(
        0,
        "Maximum SPI ID supported by "
        "secure MSI frame 7. Set to 0 to disable frame.",
    )
    MSI_S_frame7_min_SPI = Param.UInt16(
        0,
        "Minimum SPI ID supported by "
        "secure MSI frame 7. Set to 0 to disable frame.",
    )
    outer_cacheability_support = Param.Bool(
        False,
        "Allow configuration of "
        "outer cachability attributes in ITS and Redistributor",
    )
    wakeup_on_reset = Param.Bool(
        False,
        "Go against specification and start "
        "redistributors in woken-up state at reset. This allows software "
        "that was written for previous versions of the GICv3 "
        "specification to work correctly. This should not be used for "
        "production code or when the distributor is used separately from "
        "the core fast model.",
    )
    SPI_MBIS = Param.Bool(
        True, "Distributor supports meassage based signaling of SPI"
    )
    SPI_unimplemented = Param.String(
        "",
        "A comma spearated list of "
        "unimplemented SPIs ranges for sparse SPI defintion(for ex: "
        "'35, 39-42, 73)'",
    )
    irm_razwi = Param.Bool(
        False, "GICD_IROUTERn.InterruptRoutingMode is RAZ/WI"
    )
    common_LPI_configuration = Param.Unsigned(
        0,
        "Describes which "
        "re-distributors share (and must be configured with the same) "
        "LPI configuration table as described in GICR_TYPER( 0:All, "
        "1:A.x.x.x, 2:A.B.x.x, 3:A.B.C.x",
    )
    single_set_support = Param.Bool(
        False,
        "When true, forces redistributors "
        "to recall interrupts with a clear rather than issue a second Set "
        "command",
    )
    has_mpam = Param.Unsigned(
        0,
        "Implement ARMv8.4 MPAM Registers and "
        "associated functionality.\n\nPossible values of this parameter "
        "are:\n  - 0, feature is not enabled.\n  - 1, feature is "
        "implemented if ARMv8.4 is enabled.\n  - 2, feature is "
        "implemented.",
    )
    mpam_max_partid = Param.UInt16(0xFFFF, "MPAM Maximum PARTID Supported")
    mpam_max_pmg = Param.Unsigned(255, "MPAM Maximum PMG Supported")
    output_attributes = Param.String(
        "ExtendedID[62:55]=MPAM_PMG, "
        "ExtendedID[54:39]=MPAM_PARTID, ExtendedID[38]=MPAM_NS",
        "User-defined transform to be applied to bus attributes like "
        "RequestorID, ExtendedID or UserFlags. Currently, only works for "
        "MPAM Attributes encoding into bus attributes.",
    )
    has_DirtyVLPIOnLoad = Param.Bool(
        False,
        "GICR_VPENDBASER.Dirty reflects "
        "transient loading state when valid=1",
    )
    allow_LPIEN_clear = Param.Bool(
        False, "Allow RW behaviour on GICR_CTLR.LPIEN isntead of set once"
    )
    GICD_legacy_reg_reserved = Param.Bool(
        False,
        "When ARE is RAO/WI, makes "
        "superfluous registers in GICD reserved (including for the "
        "purpose of STATUSR updates)",
    )
    extended_spi_count = Param.Unsigned(0, "Number of extended SPI supported")
    extended_ppi_count = Param.Unsigned(0, "Number of extended PPI supported")
    consolidators = Param.String(
        "",
        "Specify consolidators' base addresses, "
        "interrupt line counts and base interrupt IDs, in the form "
        "'baseAddr0:itlineCount0:baseINTID0, "
        "baseAddr1:itlineCount1:baseINTID1, [etc]' "
        "(eg '0x3f100000:64:4096, 0x3f200000:64:4224'). The "
        "consolidators' count is inferred from the list (maximum of 4). "
        "If not specified, the component contains no consolidators.",
    )


class FastModelGIC(BaseGic):
    type = "FastModelGIC"
    cxx_class = "gem5::fastmodel::GIC"
    cxx_header = "arch/arm/fastmodel/GIC/gic.hh"

    sc_gic = Param.SCFastModelGIC(
        SCFastModelGIC(), "SystemC version of the GIC"
    )

    amba_m = AmbaInitiatorSocket(64, "Memory initiator socket")
    amba_s = AmbaTargetSocket(64, "Memory target socket")

    redistributor = VectorGicv3CommsInitiatorSocket(
        "GIC communication initiator"
    )

    wake_request = VectorIntSourcePin("GIC wake request initiator")

    reset = ResetResponsePort("Reset")
    po_reset = ResetResponsePort("Power on reset")

    # Used for DTB autogeneration
    _state = FdtState(addr_cells=2, size_cells=2, interrupt_cells=3)

    def get_redist_bases(self):
        """
        The format of reg_base_per_redistributor is
        '0.0.0.0=0x2c010000,0.0.0.1=0x2c020000...'
        Return an array of base addresses
        """
        redists = self.sc_gic.reg_base_per_redistributor.split(",")
        # make sure we have at least one redistributor
        assert len(redists) > 0 and "=" in redists[0]
        return [int(r.split("=")[1], 16) for r in redists]

    def get_addr_ranges(self):
        """Return address ranges that should be served by this GIC"""
        sc_gic = self.sc_gic
        gic_frame_size = 0x10000
        # Add range of distributor
        ranges = [AddrRange(sc_gic.reg_base, size=gic_frame_size)]
        # Add ranges of redistributors
        redist_frame_size = gic_frame_size * (4 if sc_gic.has_gicv4_1 else 2)
        ranges += [
            AddrRange(redist_base, size=redist_frame_size)
            for redist_base in self.get_redist_bases()
        ]
        # Add ranges of ITSs
        its_bases = [
            sc_gic.its0_base,
            sc_gic.its1_base,
            sc_gic.its2_base,
            sc_gic.its3_base,
        ]
        ranges += [
            AddrRange(its_bases[i], size=2 * gic_frame_size)
            for i in range(sc_gic.its_count)
        ]

        return ranges

    def interruptCells(self, int_type, int_num, int_trigger, int_affinity=0):
        """
        Interupt cells generation helper:
        Following specifications described in

        Documentation/devicetree/bindings/interrupt-controller/arm,gic-v3.txt
        """
        prop = self._state.interruptCells(0)
        assert len(prop) >= 3
        prop[0] = int_type
        prop[1] = int_num
        prop[2] = int_trigger
        return prop

    def generateDeviceTree(self, state):
        sc_gic = self.sc_gic

        node = FdtNode("interrupt-controller")
        node.appendCompatible(["arm,gic-v3"])
        node.append(self._state.interruptCellsProperty())
        node.append(self._state.addrCellsProperty())
        node.append(self._state.sizeCellsProperty())
        node.append(FdtProperty("ranges"))
        node.append(FdtProperty("interrupt-controller"))

        redist_stride = 0x40000 if sc_gic.has_gicv4_1 else 0x20000
        node.append(
            FdtPropertyWords(
                "redistributor-stride", state.sizeCells(redist_stride)
            )
        )

        regs = (
            state.addrCells(sc_gic.reg_base)
            + state.sizeCells(0x10000)
            + state.addrCells(self.get_redist_bases()[0])
            + state.sizeCells(0x2000000)
        )

        node.append(FdtPropertyWords("reg", regs))
        # Maintenance interrupt (PPI 25).
        node.append(
            FdtPropertyWords("interrupts", self.interruptCells(1, 9, 0x4))
        )

        node.appendPhandle(self)

        # Generate the ITS device tree
        its_frame_size = 0x10000
        its_bases = [
            sc_gic.its0_base,
            sc_gic.its1_base,
            sc_gic.its2_base,
            sc_gic.its3_base,
        ]
        for its_base in its_bases:
            its_node = self.generateBasicPioDeviceNode(
                state, "gic-its", its_base, 2 * its_frame_size
            )
            its_node.appendCompatible(["arm,gic-v3-its"])
            its_node.append(FdtProperty("msi-controller"))
            its_node.append(FdtPropertyWords("#msi-cells", [1]))
            node.append(its_node)

        yield node
